/***********************************************************************
 * APPLETGAMELOOP
 * by Marc-Henri Vuillaume
 * *********************************************************************
 * This program is free software: you can redistribute itr2 and/or modify
 * itr2 under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 *
 * This program is distributed in the hope that itr2 will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 **********************************************************************/
package mhv.fr.core;

import java.applet.Applet;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.util.Collection;
import java.util.Iterator;
import mhv.fr.sprite.Sprite;
import mhv.fr.sprite.container.SpriteContainer;
import mhv.fr.utils.Timer;

/**
 *
 * @author Marc-Henri
 */
public class AppletGameLoop extends Applet implements Runnable, KeyListener {

    /*********************************************************************
     * Applet variables
     *********************************************************************/
    private BufferedImage backbuffer; //create a double buffering objects
    private Graphics2D surface;
    private Thread gameloop;  ///the thread gameloop
    private Timer fpsTimer;
    private int frameRateCounter;
    private int fps;
    private int screenWidth;
    private int screenHeight;
    private long period;
    private int status;
    private SpriteContainer spriteContainer;
    private boolean pause;
    private boolean running;

    public boolean isPause() {
        return pause;
    }

    public void setPause(boolean pause) {
        this.pause = pause;
    }

    public boolean isRunning() {
        return running;
    }

    public void setRunning(boolean running) {
        this.running = running;
    }

    public int getStatus() {
        return status;
    }

    public void setStatus(int status) {
        this.status = status;
    }

    public BufferedImage getBackbuffer() {
        return backbuffer;
    }

    public int getFps() {
        return fps;
    }

    public long getPeriod() {
        return period;
    }

    public int getScreenHeight() {
        return screenHeight;
    }

    public int getScreenWidth() {
        return screenWidth;
    }

    public Graphics2D getSurface() {
        return surface;
    }

    public AppletGameLoop(int screenWidth, int screenHeight, int fps) {
        this.screenWidth = screenWidth;
        this.screenHeight = screenHeight;
        this.period = 1000 / fps;
        this.fps = 0;
        this.frameRateCounter = 0;
        this.fpsTimer = new Timer();
        this.backbuffer = new BufferedImage(screenWidth, screenHeight, BufferedImage.TYPE_INT_RGB);
        this.surface = backbuffer.createGraphics();
        this.spriteContainer = SpriteContainer.getInstance();
        this.running = false;
        this.pause = false;
    }

    /*********************************************************************
     * Applet Init method
     *********************************************************************/
    @Override
    public void init() {
        //launch  timer
        fpsTimer.start();
        //add listeners     
        addKeyListener(this);

        gameInit();
    } //Applet Init

    /*********************************************************************
     * Applet Start method
     *********************************************************************/
    @Override
    public void start() {
        gameloop = new Thread(this);
        gameloop.start();

    //gameInit();
    }

    /*********************************************************************
     * Applet Run method
     *********************************************************************/
    public void run() {
        //sleep parameters
        long sleepTime, timeDiff, afterTime, beforeTime;
        long overSleepTime = 0L;

        //Main loop
        beforeTime = System.nanoTime();
        Thread t = Thread.currentThread();
        while (t == gameloop) {
            try {
                if (running && !pause) {
                    gameUpdate();
                }
                repaint();

                //calculate FPS
                frameRateCounter++;
                if (fpsTimer.getTime() >= 1000) {
                    fps = frameRateCounter;
                    frameRateCounter = 0;
                    fpsTimer.reset();
                }

                //calutate sleep time
                afterTime = System.nanoTime();
                timeDiff = (afterTime - beforeTime) - overSleepTime;
                sleepTime = period * 1000000L - timeDiff;

                if (sleepTime > 0) {
                    Thread.sleep(sleepTime / 1000000L);
                    overSleepTime = (System.nanoTime() - afterTime) - sleepTime;
                } else {
                    Thread.sleep(5);
                    overSleepTime = 0L;
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            beforeTime = System.nanoTime();
        }
    }//Run

    /*********************************************************************
     * Applet Update method
     *********************************************************************/
    @Override
    public void update(Graphics g) {

        gameRender(surface);
        //repaint
        paint(g);
    }//Update

    /*********************************************************************
     * Applet Paint method
     *********************************************************************/
    @Override
    public void paint(Graphics g) {
        g.drawImage(backbuffer, 0, 0, this);
    }

    /*********************************************************************
     * Listener keypressed method
     *********************************************************************/
    public void keyTyped(KeyEvent e) {
    }

    /*********************************************************************
     * Listener keypressed method
     *********************************************************************/
    public void keyPressed(KeyEvent e) {
        int keyCode = e.getKeyCode();

        keyPressedEvent(keyCode);
    }//keyPressed

    /*********************************************************************
     * Listener keypressed method
     *********************************************************************/
    public void keyReleased(KeyEvent e) {
        int keyCode = e.getKeyCode();

        keyReleasedEvent(keyCode);
    }//keyRealed

    /*********************************************************************
     * GameUpdate method
     *********************************************************************/
    public void gameInit() {
    }

    /*********************************************************************
     * GameUpdate method
     *********************************************************************/
    public void gameUpdate() {
        spriteContainer.applyChange();
        //check for collision
        testCollisions();
        //frame update
        Collection<Sprite> _sprites = spriteContainer.getList();
        Iterator<Sprite> itr = _sprites.iterator();

        while (itr.hasNext()) {
            Sprite sp = itr.next();
            if (sp.isAlive()) {
                sp.update();
            }
        }
    }

    /*********************************************************************
     * GameRender method
     *********************************************************************/
    public void gameRender(Graphics2D g2d) {
        Collection<Sprite> _sprites = spriteContainer.getList();
        Iterator<Sprite> itr = _sprites.iterator();

        if (running) //draw sprites
        {
            while (itr.hasNext()) {
                Sprite sp = itr.next();
                if (sp.isAlive()) {
                    sp.draw();
                }
            }//while
        }//if
    }//gamerender

    /*********************************************************************
     * GameUpdate method
     *********************************************************************/
    public void keyPressedEvent(int keyCode) {
        throw new UnsupportedOperationException("Not yet implemented");
    }

    /*********************************************************************
     * GameUpdate method
     *********************************************************************/
    public void keyReleasedEvent(int keyCode) {
        throw new UnsupportedOperationException("Not yet implemented");
    }

    public long addSprite(Sprite sprite) {
        return spriteContainer.add(sprite);
    }

    public void deleteSprite(long ID) {
        spriteContainer.delete(ID);
    }

    public Sprite getSprite(long ID) {
        return spriteContainer.getSprite(ID);
    }

    public Collection<Sprite> getSpriteList() {
        return spriteContainer.getList();
    }

    private void testCollisions() {
        int size = getSpriteList().size();
        Sprite[] spriteArray = getSpriteList().toArray(new Sprite[size]);

        for (int i = 0; i < spriteArray.length; i++) {
            spriteArray[i].setCollided(false);
        }

        for (int i = 0; i < spriteArray.length; i++) {
            for (int j = i + 1; j < spriteArray.length; j++) {
                if (spriteArray[i].isAlive() && spriteArray[j].isAlive()) {
                    if (spriteArray[i].checkcollision(spriteArray[j])) {
                        spriteArray[i].setCollided(true);
                        spriteArray[j].setCollided(true);
                        spriteArray[i].getController().doCollide(spriteArray[j]);
                        spriteArray[j].getController().doCollide(spriteArray[i]);
                    }
                }
            }
        }

    } //testcollisions
}
